﻿#pragma once

#include <iostream>
#include <string>
#include "lix/text.h"
#include "lix/char_sequence.h"

namespace lix{
	namespace xml{

class Parser;
class NodeAllocator;


enum NodeType{
	kNodeRoot = 0,		//!< ルートノード			name value ともナシ
	kNodeDoctype,		//!< <!DOCTYPE  ...  />		name は !DOCTYPE、value は内容全部
	kNodeXmlDeclare,	//!< <?xml  ... ?>			name は ?xml、value は内容全部
	kNodeElement,		//!< <element></element>	name はタグ名、value はナシ
	kNodeText,			//!< 地の文					name はナシ、value は内容
	kNodeCData,			//!< <![CDATA[  ... ]]>		name はナシ、value は内容
	kNodeComment,		//!< <!--  -->				name value ともナシ
	kNodeProcInst,		//!< <?  ?>					name はナシ、value は内容全部
	kNodeAttribute,		//!< < a="b" アトリビュートもNodeとして扱う。
	kNodeInvalid,		
};


class Node {
public:
	enum { kNodeSize = 32 };


	//! コンストラクタ
	Node( NodeType type );


	//! コピーコンストラクタ
	//! 
	//! コピー可能だが、これはシャロウコピー。
	//! 同じデータを参照するのみで新しいデータが出来るわけではないので注意
	//! データ有効性の寿命は XmlDocument の寿命に依存。コピーしても同じ。
	Node( const Node& node );

	// デストラクタ、
	~Node();

	//! 代入
	Node& operator=( const Node& node );

	// ルートノードであるか返す。
	bool isRootNode() const {
		return m_Type == xml::kNodeRoot;
	}

	NodeType getType() const {
		return (NodeType)m_Type;
	}

	// 最初の子を返す。
	Node* getFirstChild() const {
		return m_Children;
	}

	// 最初の子をテキストノードとみなし、その value を返す。
	CharSequence getFirstChildText() const;

	// 最後の子を返す
	Node* getLastChild() const {
		return getLastItem(m_Children);
	}
	
	//! 子ノードの中から指定された名前を持つ最初のエレメントを返す。
	const Node* findFirstElementByName( const char* name ) const;

	const Node* getFirstAttribute() const {
		return m_Attributes;
	}

	const Node* getLastAttribute() const {
		return getLastItem(m_Attributes);
	}
	Node* getLastAttribute(){
		return getLastItem(m_Attributes);
	}
	
	//! 名前を C++ 文字列で返す。
	//! 
	//! 名前がない場合は空文字列が返却される（名前がないのと空の名前なのを判別する方法はない）
	//! 返却のため string を生成するので、それが嫌な場合は name() を使用する。
	std::string getName() const {
		return m_Name.str();
	}

	//! 値を C++ 文字列で返す。
	//! 
	//! 値がない場合は空文字列が返却される（値がないのと空の値なのを判別する方法はない）
	//! 返却のため string を生成するので、それが嫌な場合は value() を使用する。
	std::string getValue() const {
		return m_Value.str();
	}

	//! 次の兄弟ノードを返す。 
	const Node * getNextSibling() const {
		return m_Next;
	}
	
	//
	std::string getAttribute( const char* pch ) const {
		CharSequence attr = attribute( CharSequence::create( pch ));
		return attr.str();
	}
	std::string getAttribute( const std::string& attr_name ) const {
		CharSequence attr = attribute( attr_name );
		return attr.str();
	}

	

	template<class CHAR_RANGE>
	CharSequence attribute( CHAR_RANGE name_range ) const{
		Node* attr = m_Attributes;
		while( attr ){
			if( attr->name() == name_range ){
				return attr->value();
			}
			attr = attr->m_Next;
		}
		return CharSequence();
	}

	CharSequence attribute( const char* name ) const{
		return attribute( CharSequence::create( name ));
	}
	bool hasAttribute( const char* name ) const{
		Node* attr = m_Attributes;
		while( attr ){
			if( attr->name() == name ){
				return true;
			}
			attr = attr->m_Next;
		}
		return false;
	}


	const CharSequence& name() const {
		return m_Name;
	}
	const CharSequence& value() const {
		return m_Value;
	}


private:
	friend class ::lix::xml::Parser;
	friend class ::lix::xml::NodeAllocator;
	
	void addChild( Node* child );
	void addAttribute(  Node* attribute );

	static Node* getLastItem( Node* node );

	CharSequence m_Name;	//!< 名前
	CharSequence m_Value;	//!< 値
	
	Node* m_Children;		//!< 子ノード
	Node* m_Attributes;		//!< アトリビュート
	Node* m_Next;			//!< 次の兄弟

	// 仮想関数を使いたくないので、自分がナニモノかをメンバで持つ、
	uint16_t m_Type;

	// 親をたどれるようにしたいが、32byte におさまらなくなるので、諦める
	// Node* m_Parent;
};

// static_assert( sizeof(Node) <= Node::kNodeSize);


//! 子ノードフィルタ
//! 
//! アロケーションを避けるため、NodeListといったコンテナは作りたくない。
//! そのため、親から子供をたどっていく必要があるが、
//! そのスタイルで、getElementsByTagName に相当することをしたい時のためのユーティリティ
class NamedElementNodeFilter {
public:
	
	// 指定された名前を持つエレメントだけを抽出するフィルタを構築
	NamedElementNodeFilter( const std::string& name )
		: m_Name( name )
	{}

	// 名前は無視し、エレメントを抽出するフィルタを構築
	NamedElementNodeFilter()
		: m_Name("")
	{}

	const Node* firstChildOf( const Node* parent_node ){
		if( ! parent_node ){
			return NULL;
		}
		const Node* node = parent_node->getFirstChild();
		return find( node );
	}
	
	const Node* next( const Node* node ){
		node = node->getNextSibling();
		return find( node );
	}


private:
	const Node* find( const Node* node ){
		while( node ){
			if( node->getType() == kNodeElement){
				if( m_Name.empty() || node->name() == m_Name ){
					return node;
				}
			}
			node = node->getNextSibling();
			
		}
		return node;

	}

	std::string m_Name;

};







}}